import { _decorator, Component, Node, Vec2, Vec3, Quat, Camera, UITransform, view, log, Canvas, sys, macro, AnimationComponent, AnimationClip, AnimationState, ParticleSystemComponent } from 'cc';
import { playParticleOptions } from '../../../Game/Scripts/constant';

import { GameApp } from '../../../Game/Scripts/GameApp';
import { poolManager } from '../Managers/poolManager';


const { ccclass, property } = _decorator;
@ccclass("myUtil")
export default class myUtil {
    
    public static get_dayOfWeek(): number {
        const today: Date = new Date();
        const daysOfWeek: number[] = [7, 1, 2, 3, 4, 5, 6];
        const dayOfWeek: number = daysOfWeek[today.getDay()];
      //  console.log("今天是星期" + dayOfWeek);

        return dayOfWeek


    }




    public static getWeightRandIndex(weightArr: any) {
        // console.log(weightArr);
         
         //总权重
         var sumWeight = 0;
         for (var i = 0; i < weightArr.length; i++) {
         //    console.log(weightArr[i].weight);
             
             sumWeight += weightArr[i].weight;
         }
        //   console.log("sumWeight",sumWeight);
           
         //随机数 [0, sumWeight)
        // console.log("权重总计：",sumWeight);
         
         var n = (Math.random() * sumWeight) | 0;
       //  console.log("权重随机数：",n);
         
         //在总权重构成的线段上的各个起始点
         var m = 0;
         for (var i = 0; i < weightArr.length; i++) {
             var weightObj = weightArr[i];
             //如果在权重范围内
             if (n >= m && n < m + weightObj.weight) {
               //  console.log("结果：-----","随机数：",n,"m累计:",m,"权重:",weightObj.weight,"i:",i);
              
                
                 break;
             }
           //  console.log("随机数：",n,"m累计:",m,"权重:",weightObj.weight,"i:",i);
             
             m += weightObj.weight;
         }
      //   console.log(i);
         
         return i
     }







    /**
    * 将object转化为数组
    * @param { any} srcObj  
    * @returns 
    */
    public static objectToArray(srcObj: { [key: string]: any }) {

        let resultArr: any[] = [];

        // to array
        for (let key in srcObj) {
            if (!srcObj.hasOwnProperty(key)) {
                continue;
            }

            resultArr.push(srcObj[key]);
        }

        return resultArr;
    }

    /**
 * 用于数值到达另外一个目标数值之间进行平滑过渡运动效果
 * @param {number} targetValue 目标数值 
 * @param {number} curValue 当前数值
 * @param {number} ratio    过渡比率
 * @returns 
 */
    public static lerp(targetValue: number, curValue: number, ratio: number = 0.25) {
        let v: number = curValue;
        if (targetValue > curValue) {
            v = curValue + (targetValue - curValue) * ratio;
        } else if (targetValue < curValue) {
            v = curValue - (curValue - targetValue) * ratio;
        }

        return v;
    }
    /**
 * 获取两个节点坐标在xz轴的距离
 *
 * @static
 * @param {Node} ndA
 * @param {Node} ndB
 * @param {boolean} [isLocal=false] 是否为本地坐标，反之为世界坐标
 * @return {*} 
 * @memberof util
 */
    public static getTwoNodeXZLengthSqr(ndA: Node, ndB: Node, isLocal: boolean = false) {
        const aX = isLocal ? ndA.position.x : ndA.worldPosition.x;
        const aZ = isLocal ? ndA.position.z : ndA.worldPosition.z;
        const bX = isLocal ? ndB.position.x : ndB.worldPosition.x;
        const bZ = isLocal ? ndB.position.z : ndB.worldPosition.z;
        return this.getTwoPosXZLengthSqr(aX, aZ, bX, bZ);
    }
    //获取两个节点距离平方
    public static getLengthSqr(node1: Node, node2: Node): number {
        const pos1: Vec3 = node1.getWorldPosition()
        const pos2: Vec3 = node2.getWorldPosition()

        const len: number = pos2.subtract(pos1).lengthSqr()
        return len

    }




    /**
 * 获取两个坐标在xz轴的距离
 * 
 * @static
 * @param {number} aX
 * @param {number} aZ
 * @param {number} bX
 * @param {number} bZ
 * @return {*} 
 * @memberof util
 */
    public static getTwoPosXZLengthSqr(aX: number, aZ: number, bX: number, bZ: number) {
        const x = aX - bX;
        const z = aZ - bZ;
        return x * x + z * z//只拿平方
        // return Math.sqrt(x * x + z * z);
    }
    //获取两点距离
    public static getTwoPosXZLength(aX: number, aZ: number, bX: number, bZ: number) {
        const x = aX - bX;
        const z = aZ - bZ;
        // return x * x + z * z//只拿平方
        return Math.sqrt(x * x + z * z);
    }
    public static loadByFrame(total: number, countPerFrame: number, addFun: Function) {
        //  console.log("分帧加载");

        // let total = 2000
        // let countPerFrame = 30; // 每帧加载的数量
        let index = 0;  // 当前下标
        // 加载函数
        const load = () => {
            // 加载 item
            const count = Math.min(total - index, countPerFrame);
            for (let i = 1; i <= count; i++) {
                index++;

                addFun(total, countPerFrame, index)



                //  console.log("i", i, index);
            }
            // 是否还有
            if (index < total) {
                this.DelayEvent(0, load)
                //load()
                // 下一帧继续加载
                //this.scheduleOnce(() => load());
            }
        }

        load()
    }
    public static rotateNode(n: Node, r: number, t: number) {
        //旋转 可封装
        let now: Quat = new Quat()
        let dstRot: Quat = new Quat()
        const Euler: Vec3 = new Vec3(0, r, 0)//欧拉旋转
        Quat.fromEuler(dstRot, Euler.x, Euler.y, Euler.z)//新的朝向
        // 四元数得球面插值;  
        Quat.slerp(now, n.getRotation(), dstRot, t);
        n.setRotation(now);
    }

    //取数组里的最小值 下标
    public static getArrOfmin(data): number {

        //  var a = data[0];//声明变量a为最大值，获取题目数组第一个值
        var b = data[0];//声明变量b为最小值，获取题目数组第一个值
        // var aindex = 0;//a的下标
        var bindex = 0;//b的下标

        //遍历数据
        for (let i = 0; i < data.length; i++) {

            // //最大值
            // if (a < data[i]) {//对比大小，若值比a大，直接赋值给a
            //     a = data[i];
            //     aindex = i;//获取下标
            // }

            //最小值
            if (b > data[i]) {//对比大小，若值比b小，直接赋值给b
                b = data[i];
                bindex = i;//获取下标
            }
        }

        return bindex

    }
    //适配UI
    // public static AdapterUI(n: Node) {
    //     const gameWidth = GameApp.Instance.gameCanvas.width
    //     const gameHeight = GameApp.Instance.gameCanvas.height
    //     const bgUIWidth: number = n.getComponent(UITransform).width
    //     const bgUIHeight: number = n.getComponent(UITransform).height
    //     const scale_x: number = bgUIHeight / gameWidth//1920
    //     const scale_y: number = bgUIWidth / gameHeight//1080
    //     n.getComponent(UITransform).height = bgUIHeight / scale_x
    //     n.getComponent(UITransform).width = bgUIWidth / scale_y
    // }

    //横竖屏切换

    static setORIENTATION_PORTRAIT(val: boolean) {
        // 获取屏幕物理分辨率
        let w = view.getFrameSize().width;
        let h = view.getFrameSize().height;
        //   console.log("屏幕：", w, h);
        if (val && w > h) {
            //  console.log("已经是横屏,无需修改!", w, h);
            return
        } else if (!val && w < h) {
            //  console.log("已经是竖屏,无需修改!", w, h);
            return
        }
        view.setFrameSize(h, w);
        // if (sys.os == sys.OS_IOS) {
        //     console.log(">>>ios:", sys.os,sys.OS_IOS);
        //     jsb.reflection.callStaticMethod('AppController', "cocosChangeOrientation", val);
        // } else if (sys.os == sys.OS_ANDROID) {
        //     console.log(">>>android:", sys.os, sys.OS_IOS);
        //     jsb.reflection.callStaticMethod("org/cocos2dx/javascript/AppActivity", "changeOrientationH", "(Z)V", val);
        // }
        if (val) {
            //横屏
            view.setOrientation(macro.ORIENTATION_LANDSCAPE);
        }
        else {
            //竖屏
            view.setOrientation(macro.ORIENTATION_PORTRAIT);
        }
    }
    /**
   * 播放节点上的粒子特效
   *
   * @param {playParticleOptions} options
   * @return {*} 
   * @memberof EffectManager
   */
    public static playParticle2(options: playParticleOptions) {
        return new Promise((resolve, reject) => {
            //给options赋默认值
            let {
                ndEffect,
                speed = 1,
                isRecycle = false,
                recycleTime = 0,
            } = options;

            //粒子播放最长时间
            let maxDuration: number = 0;

            if (!ndEffect.active) {
                ndEffect.active = true;
            }

            let arrParticle: ParticleSystemComponent[] = ndEffect.getComponentsInChildren(ParticleSystemComponent);

            //arrParticle去掉粒子组件在没有激活和粒子组件所在节点在场景中没有激活的节点
            arrParticle = arrParticle.filter((particleCom: ParticleSystemComponent) => {
                return particleCom.node.activeInHierarchy && particleCom.enabled;
            })

            if (arrParticle.length) {
                arrParticle.forEach((element: ParticleSystemComponent) => {
                    element.simulationSpeed = speed;
                    element?.clear();
                    element?.stop();
                    element?.play();

                    let duration: number = element.duration;
                    maxDuration = duration > maxDuration ? duration : maxDuration;
                })

                //优先使用传进来的时间，如果没有，则使用粒子最长时间（后续如果能监听粒子播放结束状态，则再优化写法）
                maxDuration = recycleTime && recycleTime > 0 ? recycleTime : maxDuration;

                setTimeout(() => {
                    if (ndEffect && ndEffect.parent) {
                        if (isRecycle) {
                            // console.log(`###回收粒子特效节点: ${ndEffect.name}`);
                            poolManager.instance.putNode(ndEffect);
                        }

                        // console.log(`###节点上的粒子播放完成: ${ndEffect.name}`);
                        resolve?.(null);
                    }
                }, maxDuration * 1000)

            } else {
                console.warn(`###${ndEffect.name}节点下没有可以播放的粒子组件，直接返回播放完成状态`);
                resolve?.(null);
            }
        })
    }
    /**
     * 播放节点上的粒子特效
     *
     * @param {Node} ndEffect 特效节点
     * @param {number} [speed=1] 粒子播放速度
     * @param {boolean} [isRecycle=false] 是否需要回收特效节点
     * @param {(number | null)} [recycleTime] 回收时间, 如果为null则使用maxDuration
     * @param {(Function | null)} [callback] 回调函数
     * @returns 返回播放完成所需秒数
     * @memberof EffectManager
     */
    public static playParticle(ndEffect: Node, speed: number = 1, isRecycle: boolean = false, recycleTime?: number | null, callback?: Function | null) {
        //粒子播放最长时间
        let maxDuration: number = 0;

        if (!ndEffect.active) {
            ndEffect.active = true;
        }

        let arrParticle: ParticleSystemComponent[] = ndEffect.getComponentsInChildren(ParticleSystemComponent);

        if (arrParticle.length) {
            arrParticle.forEach((element: ParticleSystemComponent) => {
                element.simulationSpeed = speed;
                element?.clear();
                element?.stop();
                element?.play();

                let duration: number = element.duration;
                maxDuration = duration > maxDuration ? duration : maxDuration;
            })

            //使用传进来的回收时间，否则设置为时长最长
            maxDuration = recycleTime && recycleTime > 0 ? recycleTime : maxDuration;

            if (callback || isRecycle) {
                setTimeout(() => {
                    if (ndEffect && ndEffect.parent) {
                        callback && callback();

                        if (isRecycle) {
                            poolManager.instance.putNode(ndEffect);
                        }
                    }
                }, maxDuration * 1000)
            }

            return maxDuration;
        } else {
            // console.warn(`###${ndEffect.name}节点下没有粒子特效`);
            return 0;
        }
    }

    /**
     * 播放节点上的默认动画特效
     *
     * @param {Node} ndEffect 特效节点
     * @param {number} [speed=1] 动画播放速度
     * @param {(string | null)} animationName 动画名称（当节点下只有一个动画组件，并指定播放动画的时候才会使用这个参数，否则都使用默认动画）
     * @param {boolean} [isLoop=false] 是否循环播放
     * @param {boolean} [isRecycle=false] 是否回收
     * @param {(number | null)} recycleTime 回收时间,如果为null则使用maxDuration
     * @param {(Function | null)} callback 回调函数
     * @returns
     * @memberof EffectManager
     */
    public static playAnimation(ndEffect: Node, speed: number = 1, animationName: string | null, isLoop: boolean = false, isRecycle: boolean = false, recycleTime?: number | null, callback?: Function | null) {
        //动画播放最长时间
        let maxDuration: number = 0;
        let aniState: AnimationState = null!;

        if (!ndEffect.active) {
            ndEffect.active = true;
        }

        let arrAni: AnimationComponent[] = ndEffect.getComponentsInChildren(AnimationComponent);

        if (arrAni.length) {
            arrAni.forEach((element: AnimationComponent, idx: number) => {
                let aniName = animationName ? animationName : element?.defaultClip?.name;

                if (aniName) {
                    aniState = element.getState(aniName);
                    if (aniState) {
                        aniState.time = 0;
                        aniState.speed = speed;
                        aniState.sample();

                        let duration = aniState.duration;
                        maxDuration = duration > maxDuration ? duration : maxDuration;

                        if (isLoop) {
                            aniState.wrapMode = AnimationClip.WrapMode.Loop;
                        } else {
                            aniState.wrapMode = AnimationClip.WrapMode.Normal;
                        }

                        element?.play(aniName);
                    }
                }
            })

            maxDuration = recycleTime && recycleTime > 0 ? recycleTime : maxDuration;

            let cb = () => {
                if (ndEffect && ndEffect.parent) {
                    callback && callback();

                    if (isRecycle) {
                        poolManager.instance.putNode(ndEffect);
                    }
                }
            }

            if (callback || isRecycle) {
                if (arrAni.length === 1) {
                    arrAni[0].once(AnimationComponent.EventType.FINISHED, () => {
                        cb();
                    })
                } else {
                    setTimeout(() => {
                        cb();
                    }, maxDuration * 1000)
                }
            }

            return maxDuration;
        } else {
            //console.warn(`###${ndEffect.name}节点下没有动画特效`);
            return 0;
        }
    }
    /**
       * 播放节点下面的动画、粒子
       *
       * @param {Node} ndEffect 特效节点
       * @param {boolean} [isPlayAnimation=true] 是否播放动画
       * @param {boolean} [isPlayParticle=true] 是否播放特效
       * @param {number} [speed=1] 播放速度
       * @param {boolean} [isRecycle=false] 是否回收
       * @param {number} [recycleTime=0] 回收时间
       * @param {(Function | null)} callback 回调函数
       * @returns
       * @memberof EffectManager
       */
    public static playEffect(ndEffect: Node, isPlayAnimation: boolean = true, isPlayParticle: boolean = true, speed: number = 1, isRecycle: boolean = false, recycleTime?: number | null, callback?: Function | null) {
        //特效最长持续时间
        let maxDuration: number = 0;

        if (isPlayAnimation) {
            let duration = myUtil.playAnimation(ndEffect, speed, null, false, false, 0, null);
            maxDuration = duration > maxDuration ? duration : maxDuration;

        }

        if (isPlayParticle) {
            let duration = this.playParticle(ndEffect, speed, false, 0, null);
            maxDuration = duration > maxDuration ? duration : maxDuration;
        }

        maxDuration = recycleTime && recycleTime > 0 ? recycleTime : maxDuration;

        if (callback || isRecycle) {
            setTimeout(() => {
                if (ndEffect.parent) {
                    callback && callback();

                    if (isRecycle) {


                        poolManager.instance.putNode(ndEffect);

                    } else {
                        ndEffect.destroy();

                    }
                }
            }, maxDuration * 1000)
        }
    }
    static physicsMoveFactor: number = 70;

    /**
     * 返回 min ~ max 的随机数
     * @param min 最小值（包含）
     * @param max 最大值（包含）
     */
    static Random(min: number, max: number): number {
        return Math.floor(Math.random() * (max - min + 1) + min);
    }

    /**
     * 求两点之间的距离
     * @param point1 点1
     * @param point2 点2
     */
    static Distance(point1: Vec2, point2: Vec2): number {
        return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
    }

    //直线与坐标轴的夹角 （斜率转角度） 返回的是一个弧度值
    static Angle(point1: Vec2, point2: Vec2): number {
        let x = point2.x - point1.x;
        let y = point2.y - point1.y;
        return Math.atan2(y, x);
    }

    /**
     * 将秒数格式化为00:00
     * @param second 秒数
     */
    static GetTimeFormat(second: number) {
        let mStr = "";
        let sStr = "";
        let m = Math.floor(second / 60);
        let s = second % 60;
        if (m < 10)
            mStr = "0" + m;
        else
            mStr = m.toString();
        if (s < 10)
            sStr = "0" + s;
        else
            sStr = s.toString();
        return "mm:ss".replace("mm", mStr).replace("ss", sStr);
    }

    /**
     * 获取当前时间戳。得到距 1970 年 1 月 1 日之间的毫秒数，13位
     */
    static GetNowTimestamp(): number {
        return new Date().getTime();
    }

    /**
     * 时间戳转换日期
     * @param timestamp 时间戳，13位
     */
    static TimestampToDate(timestamp): string {
        let time = parseInt(timestamp);
        if (timestamp.length < 13) //要转化为毫秒
            time = time * 1000;

        return new Date(time).toLocaleString();
    }

    //深拷贝
    static DeepClone(obj): any {
        let objClone = Array.isArray(obj) ? [] : {};
        if (obj && typeof obj === "object") {
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    //判断ojb子元素是否为对象，如果是，递归复制
                    if (obj[key] && typeof obj[key] === "object") {
                        objClone[key] = myUtil.DeepClone(obj[key]);
                    } else {
                        //如果不是，简单复制
                        objClone[key] = obj[key];
                    }
                }
            }
        }
        return objClone;
    }

    //延迟
    static Delay(ms: number) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    //延迟调用
    static async DelayEvent(ms: number, event: Function) {
        await myUtil.Delay(ms);
        if (event)
            event();
    }

    //使节点看向某位置  （Cocos中的LookAt是看向-Z轴，这里是看向正Z轴）
    static LookAt(node: Node, pos: Vec3, up?: Vec3): void {
        const q_a = new Quat();
        const v3_a = new Vec3();
        node.getWorldPosition(v3_a);
        Vec3.subtract(v3_a, pos, v3_a);
        Vec3.normalize(v3_a, v3_a);
        Quat.fromViewUp(q_a, v3_a, up);
        node.setWorldRotation(q_a);
    }

    //获取屏幕缩放比 一般用于坐标转换时为了适应不同分辨率
    static GetViewScale(): Vec2 {
        const scaleX = view.getScaleX();
        const scaleY = view.getScaleY();
        return new Vec2(scaleX, scaleY);
    }

    //世界坐标转屏幕坐标
    static GetScreenPos(wpos: Vec3, camera: Camera): Vec3 {
        // 获取 3D 相机里的相机数据
        const cam = camera.camera;
        const pos = new Vec3();

        // 将 3D 节点的世界坐标转换到屏幕坐标
        cam.worldToScreen(pos, wpos);

        return pos;
    }

    //屏幕坐标转ui坐标
    static ScreenToUIPos(screenPos: Vec3, uiNode: Node) {

        const scaleX = view.getScaleX();
        const scaleY = view.getScaleY();

        // const locationUI = screenPos;
        const locationUI = new Vec3(screenPos.x / scaleX, screenPos.y / scaleY, screenPos.z);
        // log("screenPos: " + screenPos + " locationUI: ", locationUI);

        const uiSpaceWorldPos = new Vec3(locationUI.x, locationUI.y, 0);
        const nodeAUITrans = uiNode.getComponent(UITransform)!;
        // 转换到 nodeA 节点下的本地坐标（该值也就是相对于 nodeA 的值）
        const pos = new Vec3();
        nodeAUITrans.convertToNodeSpaceAR(uiSpaceWorldPos, pos);
        return pos;
    }

    //世界坐标转ui坐标
    static WorldToUIPos(worldPos: Vec3, camera: Camera, uiNode: Node) {
        const screenPos = myUtil.GetScreenPos(worldPos, camera);
        return myUtil.ScreenToUIPos(screenPos, uiNode);
    }

    //弧度转欧拉角
    static RadianToEuler(radian: number): number {
        return radian * 180 / Math.PI;
    }

    //随机获得一个X-Z平面的发射方向
    static GetRandomDirection(): Vec3 {
        let x = Math.random() * 2 - 1;
        let z = Math.random() * 2 - 1;
        return new Vec3(x, 0, z).normalize();
    }

    //对一个方向偏移一定角度
    static GetDirOffset(dir: Vec3, offset: number): Vec3 {
        let angle = offset * Math.PI / 180;
        let x = dir.x * Math.cos(angle) - dir.z * Math.sin(angle);
        let z = dir.x * Math.sin(angle) + dir.z * Math.cos(angle);
        return new Vec3(x, 0, z).normalize();
    }
}


